import constants from '@/constants';
import { BaseService } from '@/core/base.service';
import { HuifuService } from '@/core/huifu.service';
import { SandService } from '@/core/sand.service';
import { UtilService } from '@/core/util.service';
import { YopService } from '@/core/yop.service';
import {
  OrderStatusEnum,
  OrderTypeEnum,
  PaymentTypeEnum,
  TokenStatusEnum,
} from '@/enums';
import { OrderService } from '@/services/order.service';
import { halt } from '@/utils';
import { InjectQueue, Process, Processor } from '@nestjs/bull';
import { Cron, CronExpression } from '@nestjs/schedule';
import { Job, Queue } from 'bull';
import dayjs from 'dayjs';
import { Logger } from "@nestjs/common";

@Processor('order')
export class OrderProcessor extends BaseService {
  constructor(
    @InjectQueue('order')
    private readonly orderQueue: Queue,
    private readonly utilService: UtilService,
    private readonly orderService: OrderService,
    private readonly yopService: YopService,
    private readonly huifuService: HuifuService,
    private readonly sandService: SandService,
  ) {
    super();
  }

  async onModuleInit() {
    await this.utilService.cleanKeys(`bull:order:repeat`);
    await this.orderQueue.add('polling', null, {
      repeat: {
        every: 30000,
      },
      jobId: 'order-polling',
    });
    // await this.orderQueue.add('tz', null, {
    //   repeat: {
    //     every: 60 * 1000,
    //   },
    //   jobId: 'order-tz',
    // });
  }

  @Cron(CronExpression.EVERY_30_MINUTES)
  async tzEvery30Minutes() {
    const date = dayjs().format('YYYY-MM-DD HH:mm:ss');
    console.log(`${date} - 同步订单时区`);
    if (!!constants.isDev) {
      return;
    }
    // 用于记录账单的东八区日期
    await this.prisma.$executeRawUnsafe(`
      UPDATE \`Order\` AS t
      SET t.date = DATE_FORMAT(CONVERT_TZ(t.createdAt, 'UTC', 'Asia/Shanghai'), '%Y-%m-%d')
      WHERE t.createdAt >= '${dayjs().subtract(1, 'hours').format('YYYY-MM-DD 00:00:00')}'
    `);
  }

  @Cron(CronExpression.EVERY_HOUR)
  async tzEveryHour() {
    const date = dayjs().format('YYYY-MM-DD HH:mm:ss');
    console.log(`${date} - 同步账单时区`);
    if (!!constants.isDev) {
      return;
    }
    // 用于记录账单的东八区日期
    await this.prisma.$executeRawUnsafe(`
      UPDATE \`Order\` AS t
      SET t.date = DATE_FORMAT(CONVERT_TZ(t.createdAt, 'UTC', 'Asia/Shanghai'), '%Y-%m-%d')
      WHERE t.createdAt >= '${dayjs().subtract(3, 'days').format('YYYY-MM-DD 00:00:00')}'
    `);
  }

  @Process('polling')
  async polling() {
    const date = dayjs().format('YYYY-MM-DD');
    console.log(`${date} - 支付订单轮询`);
    if (!!constants.isDev) {
      Logger.debug('is dev return');
      return;
    }
    try {
      const orders = await this.prisma.order.findMany({
        where: {
          status: OrderStatusEnum.PENDING,
          isDeleted: false,
          createdAt: {
            lte: dayjs().subtract(5, 'minutes').toDate(),
          },
        },
      });
      for (let o of orders) {
        Logger.debug(
          `支付订单 ${o.no} ${dayjs(o.createdAt).format('YYYY-MM-DD HH:mm:ss')}`,
        );
        await this.orderQueue.add('cancel', {
          no: o.no,
        });
      }
    } catch (e) {
      console.log(e);
    }
  }

  @Process('cancel')
  async cancel(job: Job<{ no: string }>) {
    const { no } = job.data;
    Logger.debug(`订单取消: ${no}`);
    return await this.prisma.$transaction(async (tx) => {
      const order = await tx.order.findFirst({
        where: {
          no,
          status: OrderStatusEnum.PENDING,
        },
        select: {
          id: true,
          planId: true,
          type: true,
          payType: true,
          payNo: true,
          createdAt: true,
          isTargetedSale: true,
          owner: {
            select: {
              id: true,
              mobile: true,
            },
          },
        },
      });

      if (!order) return;

      if (!this.config.get('isUseBalance')) {
        if (order.payType === PaymentTypeEnum.YOP) {
          const res = await this.yopService.orderQuery(no);
          console.log(
            `易宝订单${no}状态: `,
            res?.result?.code,
            res?.result?.message,
          );
          if (!res || !res.isSuccess || res?.result?.code !== 'OPR00000') {
            return halt(res?.result?.message ?? '易宝订单查询异常');
          }
          if (res.result?.status === 'SUCCESS') {
            return await this.orderService.completeOrderByNo(no);
          }
        } else if (order.payType === PaymentTypeEnum.HUIFU) {
          const res = await this.huifuService.qry008({
            order_id: no,
            order_date: dayjs(order.createdAt).format('YYYYMMDD'),
            trans_type: '36',
          });
          console.log(`汇付订单${no}状态: `, res?.resp_code, res?.resp_desc);
          if (!res || res?.resp_code !== 'C00000') {
            return halt(res?.resp_desc ?? '汇付单查询异常');
          }
          if (['I', 'P'].includes(res.trans_stat)) {
            console.log(`汇付订单取消失败: ${res.trans_stat}, 稍后重试`, no);
            await this.orderQueue.add(
              'cancel',
              {
                no,
              },
              {
                delay: dayjs().add(1, 'minutes').diff(dayjs()),
              },
            );
            return;
          }
          if (res?.trans_stat === 'S') {
            return await this.orderService.completeOrderByNo(no);
          }
        } else if (order.payType === PaymentTypeEnum.SNAD) {
          if (order.type === OrderTypeEnum.INITIAL) {
            const res = await this.sandService.query(no);
            console.log(
              `杉徳一级订单${no}状态: `,
              res?.head?.respCode,
              res?.head?.respMsg,
            );
            if (!!res) {
              if (res?.head?.respCode === '000000') {
                if (res?.body?.orderStatus === '00') {
                  return await this.orderService.completeOrderByNo(no);
                }
              }
            }
          }
          if (order.type === OrderTypeEnum.MARKET) {
            const res = await this.sandService.queryC2C(no);
            console.log(
              `杉徳二级订单${no}状态: `,
              res?.response?.responseCode,
              res?.response?.responseDesc,
            );
            if (!!res) {
              if (['01', '05', '06'].includes(res?.response?.orderStatus)) {
                console.log(
                  `杉徳订单取消失败: ${res.trans_stat}, 稍后重试`,
                  no,
                );
                await this.orderQueue.add(
                  'cancel',
                  {
                    no,
                  },
                  {
                    delay: dayjs().add(1, 'minutes').diff(dayjs()),
                  },
                );
                return;
              }
              if (res?.response?.orderStatus === '00') {
                return await this.orderService.completeOrderByNo(no);
              }
            }
          }
        } else {
          return halt(`订单取消: ${no}异常`);
        }
      }

      const subOrders = await tx.subOrder.findMany({
        where: {
          orderId: order.id,
        },
      });

      for (let o of subOrders) {
        Logger.debug(o.tokenId);
        await tx.token.update({
          where: {
            id: o.tokenId,
            status: TokenStatusEnum.LOCKING,
          },
          data: {
            status: !!order.isTargetedSale
              ? TokenStatusEnum.HOLD
              : order.type === OrderTypeEnum.INITIAL
                ? TokenStatusEnum.HOLD
                : TokenStatusEnum.ON_SALE,
          },
        });
      }

      await tx.order.update({
        where: {
          id: order.id,
        },
        data: {
          status: OrderStatusEnum.CANCELED,
        },
      });

      for (let o of subOrders) {
        if (order.type === OrderTypeEnum.INITIAL) {
          await this.redis.lrem(`plan:total:${order.planId}`, 1, o.tokenId);
          await this.redis.lrem(
            `plan:user:${order.planId}:${order.owner.mobile}`,
            1,
            o.tokenId,
          );
        }
      }

      Logger.debug('取消订单', subOrders);

      if (subOrders.length > 1) {
        await this.redis.set(
          `market:limit:${order.owner.id}`,
          order.owner.mobile,
          'EX',
          300,
        );
      }

      const key = `order:cancel:${order.owner.id}`;
      await this.redis.lpush(key, no);
      await this.redis.expire(key, 30 * 60);

      const len = await this.redis.llen(`order:cancel:${order.owner.id}`);
      if (!!len && Number(len) >= 3) {
        await this.redis.set(
          `order:limit:${order.owner.id}`,
          order.owner.mobile,
          'EX',
          180,
        );
      }
    });
  }
}
